#ifndef __COMMON_H__
#define __COMMON_H__

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<pthread.h>
#include<stdbool.h>
#include<sys/socket.h>
#include<sys/prctl.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#define _GNU_SOURCE
#include<sys/syscall.h>
#include<stdarg.h>
#include<signal.h>
#include<sys/epoll.h>
#include<sys/time.h>
#include<sys/resource.h>
#include<signal.h>
#include<pwd.h>
#include<glib.h>
#include<ctype.h>
#include<zlib.h>
#include<assert.h>


#define MAX_HTTP_HEADERS  20 

/*
 *
 *
 */







/*
 *@
 */
struct httpMultiPartStream{
	char *boundary;
	int boundaryLen;
	char *varName;
	char *fileName;
	void *userData;
	int prevIoLen;
	int state;
	int processingPart; //计数器;
};




/*
 *@HTTP处理逻辑状态机
 */
enum httpConnectionStat{
   CONNECTION_INITIAL = 1,
   RECV_REQUEST = 2,
   PARSE_REQUEST = 3,
   SEND_RESPONSE = 4,
   CONNECTION_CLOSE = 5,
   HANDLE_REQUEST = 6
};

/*
 *@和网络连接关联的http属性数据
 */
struct httpOpt{
	/*
	 *@member1;
	 */
	FILE *fp;//文件流;
	int64_t contentLen; //文件的正文长度;
	int64_t sended; //已经发送了多少字节;
	int keepAlive;//保持keepAlive;
	struct httpMultiPartStream  mpStream; //multipart 流;
};


/*
 *@brief:
 */
struct signal_st{
	int signal;
	void (*signalHandle)(int);
};


/*
 *@消息类型;
 */
enum msgtype{
	NEWCONNECTION = 0x1,
	SYSTEMEXIT = 0x02 
};

/*
 *@系统信息结构体;
 */
struct systemmsg_st{
	int msgType;
	int msgData;
	struct sockaddr_in peeraddr;
};

/*
 *@动态字符串
 */
struct dynString{
	char *str;
	size_t len;
};

/*@brief:
 *
 */
struct httpMessage{
	struct dynString message;
	struct dynString body;
	struct dynString method;  //http方法;
	struct dynString uri;     //http URI;
	struct dynString version;  //http 版本;
	int responseCode;  //响应码;
	struct dynString responseStatusMsg;  //响应消息体;
	struct dynString queryString;
	/*
	 *@报文头部;
	 */
	struct dynString headerNames[MAX_HTTP_HEADERS];
	struct dynString headerValues[MAX_HTTP_HEADERS];
};

/*@brief:跟http服务相关的配置;
 *
 */
struct httpServerCtx{
	 char *rootDirPath;
	 char *indexHtmlPath;
	 char *enable_directory_listing;
	 bool gzipOnFlag; //gzip压缩开关;
};


/*
 *@brief: 
 */
struct connection{
	int connfd;
    int state;
	z_stream stream;
	bool gzipOn;
	size_t currDataLen;
	size_t (*readData)(int fd, unsigned char *buf, size_t dataSize);
	size_t (*writeData)(int fd, unsigned char *buf, size_t dataSize);		
	time_t start_time;
	bool   onclose;
	time_t lasttime;
	unsigned char * recvbuf;
	size_t bufSize;
	unsigned char * sendbuf;
	struct httpOpt httpopt;
};


#define BACKLOG_MAX  10000
#define DEFAULT_TIMEOUT  200
#define CONFIGFILEPATH "./webserver.ini"

/*
 *@brief:
 */
enum optionIndex{
	SERVER_IP_OPT = 0,
	SERVER_PORT_OPT,
	WORKER_THREAD_NUM_OPT,
	DEBUG_LEVEL_OPT,
	KEEP_ALIVE_OPT,
	MAX_CONNECT_NUM_OPT, 
	LOG_OUT_PUT_MODE_OPT,
	LOG_OUT_PUT_PATH_OPT,
	LOG_BUF_LEN_OPT,
	LOG_BUF_BLOCK_NUM_OPT,
	DOCUMENT_ROOT_OPT,
	INDEX_HTML_PATH_OPT,
	ENABLE_DIRECTORY_LISTING_OPT,
	HTTP_GZIP_SWITCH_OPT,
};


#define RESET_CONNECTION(x) do { \
	(x)->start_time = 0;  \
	(x)->lasttime = 0; \
	(x)->readData = (x)->writeData = NULL; \
	(x)->connfd = -1; \
	(x)->currDataLen = 0; \
	(x)->bufSize = 0;\
	if((x)->recvbuf != NULL){\
		free((x)->recvbuf); \
		(x)->recvbuf = NULL; \
	} \
	if((x)->sendbuf != NULL){ \
		free((x)->sendbuf); \
		(x)->sendbuf = NULL; \
	} \
	memset(&((x)->httpopt), 0, sizeof(struct httpOpt)); \
}while(0)


#define INIT_CONNECTION(x, y, z) do { \
    (x)->state = CONNECTION_INITIAL; \
	(x)->onclose = false; \
	(x)->start_time  = time(NULL); \
	(x)->lasttime = (x)->start_time; \
	(x)->readData = readDataFromClient; \
	(x)->writeData = writeDataToClient; \
	(x)->connfd = (y);  \
	(x)->bufSize = (z); \
	(x)->currDataLen = 0; \
	(x)->recvbuf = (unsigned char *)malloc((z)); \
    (x)->sendbuf = (unsigned char *)malloc((z)); \
	memset((x)->recvbuf, '\0', (z)); \
	memset((x)->sendbuf, '\0', (z)); \
	memset(&((x)->httpopt), 0, sizeof(struct httpOpt)); \
}while(0)





/*
 *@返回值;
 */
enum retvalue{
	RET_ERROR = -1,
	RET_OK = 0
};


//debug 函数
#define DEBUG_TRACE(y) do {  \
	flockfile(stdout); \
	printf y;  \
	funlockfile(stdout);  \
}while(0)


enum iotype{
	EPOLL_MODULE = 0,
	SELECT_MODULE = 1,
	POLL_MODULE,
};

/*
 *@brief:
 */
struct ioCtx{
	int epollfd;
	struct epoll_event *eventset;
	int (*ioInit)(struct ioCtx **io);
	int (*ModEvent)(struct ioCtx  *io, int fd, int op, int events);
	int (*delEvent)(struct ioCtx  *io, int fd);
	int (*ioWaitEvent)(struct ioCtx *io, int timeout);
	void (*deInit)(struct ioCtx *io);
};


enum OutPutMode{
	ONE_OUTPUT = 0,
	MULTI_OUTPUT 
};

/*
 *@brief:日志的上下文;
 */
struct logOutPut{
	int OutPutMode; //日志输出模式; 单条或者批量;
	struct iovec *ivec;
	size_t iovcnt;  //预设的缓存块个数;
	size_t buffLen; //设定的缓冲区长度;
	size_t currindex;
	int logfd;
	char *logPath;
	char *mapAddr; //映射内存地址;
};



/*
 *@brief:
 */
struct workerPthread_ctx{
	pthread_t tid;
	int iotype;
	void *ctx; //服务的上下文;
	struct ioCtx *workerIo;
	struct connection *connpool;  //管理网络连接的
	size_t connpoolsize;	
	struct httpServerCtx *httpctx; //处理http 
	int listensock;
	bool bStopFlag;
	uint64_t currentConnNum;
};



/*
 *@服务配置;
 */
struct serverConfigOpt{
	GKeyFile *config;
	char *serverIp;
	short listenPort;
	bool keepAlive;
	int keepAliveTime;
	int threadNum;
	int OutPutMode;
	size_t maxconnSize;  
	char *outPutLogPath;
	int debugLevel;
	size_t buffLogLen; 
	size_t buffBlockNum;
    char *rootDirPath;
	char *indexHtmlPath;
	char *enable_directory_listing;
	bool gzipOnFlag;
};


/*
 *@服务上下文;
 */
struct webServerCtx{
	bool bStopFlag;
	int  iotype;
	struct serverConfigOpt  *opt;
	struct logOutPut *log; //日志上下文; 
	struct workerPthread_ctx  *worker; //工作线程的
	int listenfd; //监听套接字;
	struct ioCtx *io;
	int  *workqueue;  //主线程和工作线程通信套接字;
	volatile int currentIndex;
	volatile int TotalthreadNum;
	struct sockaddr_in serverAddr;
};


extern int errno;
extern struct webServerCtx *ctx;
extern volatile int totalthreadNum;
extern volatile pthread_mutex_t  mtx;
#define isExistFile(x)  access((x), F_OK)

#define openFile(x, y)  open((x), (y), 0644) 




#define isNotZero(x) (((x) > 0 ) ? RET_OK : RET_ERROR)




#endif 
